#ifndef GAME_WORLD_H
#define GAME_WORLD_H

#include "game_platform.h"
#include "game_asset.h"
#include "game_memory.h"

#define TILES_PER_CHUNK 8
#define TILE_CHUNK_UNINITIALIZED INT32_MAX
introspect(category : "world") struct world_position {
    // TODO: 现在我们能去掉 abstile 吗？
    // 并且仍然允许引用实体能够
    // 确定它们的位置（或者更确切地说，它们在哪个 world_chunk 中？）

    // 块的 X 坐标，表示该块在世界中的水平方向位置
    int32 ChunkX;

    // 块的 Y 坐标，表示该块在世界中的垂直方向位置
    int32 ChunkY;

    // 块的 Z 坐标，表示该块在世界中的深度方向位置
    int32 ChunkZ;

    // 注意：这些是从瓦片中心的偏移量
    // 实际位置在当前瓦片内相对于瓦片左下角的 X 轴偏移量（小数精度）
    // 实际位置在当前瓦片内相对于瓦片左下角的 Y 轴偏移量（小数精度）
    // 实际位置在当前瓦片内相对于瓦片左下角的 Z 轴偏移量（小数精度）
    v3 Offset_;
};
// 定义一个内联函数，用于生成一个无效的（空）世界位置。
inline world_position NullPosition(void) {
    world_position Result{};

    // 将所有的块坐标（ChunkX、ChunkY、ChunkZ）初始化为未初始化状态的标志值。
    // TILE_CHUNK_UNINITIALIZED 是一个特定的标志值，用于表示此位置尚未被赋予有效数据。
    Result.ChunkX = TILE_CHUNK_UNINITIALIZED;
    Result.ChunkY = TILE_CHUNK_UNINITIALIZED;
    Result.ChunkZ = TILE_CHUNK_UNINITIALIZED;

    // 返回标志为无效位置的 world_position。
    return Result;
}

// 定义一个内联函数，用于检查某个 world_position 是否为有效位置。
inline bool32 IsValid(world_position P) {
    // 检查给定位置 P 的每个坐标是否均不等于未初始化标志值。
    // 如果所有坐标均不等于 TILE_CHUNK_UNINITIALIZED，则说明该位置是有效位置。
    // 注意：使用的是逻辑与操作符 "&&"，只有所有条件均满足时，结果才为真。
    bool32 Result = ((P.ChunkX != TILE_CHUNK_UNINITIALIZED) &&  // 检查 ChunkX 是否初始化
                     (P.ChunkY != TILE_CHUNK_UNINITIALIZED) &&  // 检查 ChunkY 是否初始化
                     (P.ChunkZ != TILE_CHUNK_UNINITIALIZED));   // 检查 ChunkZ 是否初始化
    // 返回结果。如果所有坐标均已初始化，则此位置为有效位置（返回 true）；否则为无效位置（返回
    // false）。
    return (Result);
}

// 这是一个表示瓦片实体块的结构体，用于存储与瓦片相关的多个实体。
// 其中包含了实体的数量、实体数组和指向下一个瓦片实体块的指针。
struct world_entity_block {
    uint32 EntityCount;  // 记录当前瓦片实体块中存储的实体数量。
    uint32 LowEntityIndex[16];
    // 是一个数组，用于存储 16 个低频实体的索引。每个索引对应一个低频实体，数组的大小为
    // 16，表示最多可以存储 16
    // 个低频实体的索引位置。这个索引可以用来快速访问对应的低频实体，优化查询和操作这些实体的效率。
    world_entity_block *Next;  // 指向下一个瓦片实体块的指针，形成链表结构以存储更多实体。

    uint32 EntityDataSize;
    uint8 EntityData[1 << 16];
};

// 瓦片地图结构体，用于存储瓦片地图的信息和瓦片数据
struct world_chunk {
    // 瓦片块在整个地图网格中的 X 轴位置
    int32 ChunkX;

    // 瓦片块在整个地图网格中的 Y 轴位置
    int32 ChunkY;

    // ChunkZ表示瓦片所在的Z轴层块的索引。
    int32 ChunkZ;

    // 定义一个名为 FirstBlock 的变量，类型为 world_entity_block。
    // 它是一个瓦片实体块，用于存储与瓦片相关的实体。
    // 这是瓦片实体链表的第一个块，可以通过链表结构扩展更多的实体块。
    // TODO: 对此进行性能分析，并确定是否使用指针会更好！
    world_entity_block *FirstBlock;

    // 指向哈希表中下一个冲突项的指针
    world_chunk *NextInHash;
    // 指向哈希表中当前槽的位置的下一个 tile_chunk。如果该槽中存在冲突（即其他
    // world_chunk 也映射到了相同的哈希值），则 `NextInHash`
    // 指向这些冲突项中的下一个 tile_chunk，以形成链表形式进行处理。
};

struct world {
    memory_arena Arena;

    // 哈希表数组，用于存储 world_chunk 结构体
    // TODO: 如果瓷砖实体块继续直接大量存储在瓷砖块中，ChunkHash 可能应该切换为指针。
    world_chunk *ChunkHash[4096];
    // 这是一个大小为 4096 的数组，每个元素都是一个指向 World_chunk
    // 结构体的指针。用于将 World_chunk
    // 按其计算出的哈希值存储在合适的槽中。通过这种方式，可以高效地管理和查找
    //  world_chunk 以优化游戏或其他应用程序中的数据结构。

    // real32 TileSideInMeters;
    // real32 TileDepthInMeters;
    v3 ChunkDimInMeters;  // 表示块的三维维度，以米为单位，分别对应 X、Y 和 Z 方向的大小。
    world_chunk *FirstFree;
    world_entity_block *FirstFreeBlock;  // 指向第一个空闲的实体块。用于管理内存池中的空闲块列表。
    world_chunk *FirstFreeChunk;
};

inline void AddBlockToFreeList(world *World, world_entity_block *Old) {
    Old->Next = World->FirstFreeBlock;
    World->FirstFreeBlock = Old;
}
inline void AddChunkToFreeList(world *World, world_chunk *Old) {
    Old->NextInHash = World->FirstFreeChunk;
    World->FirstFreeChunk = Old;
}

inline void ClearWorldEntityBlock(world_entity_block *Block) {
    Block->EntityCount = 0;
    Block->Next = 0;
    Block->EntityDataSize = 0;
}
struct low_entity;
struct audio_state;
struct playing_sound;

void ChangeEntityLocation(memory_arena *Arena, world *World, uint32 LowEntityIndex,
                          low_entity *LowEntity, world_position NewPInit);
inline bool32 IsCanonical(real32 ChunkDim, real32 TileRel);
bool32 IsCanonical(world *World, v3 Offset);
inline bool32 AreInSameChunk(world *World, world_position *A, world_position *B);
world_chunk *RemoveWorldChunk(world *World, int32 ChunkX, int32 ChunkY, int32 ChunkZ);
void RecanonicalizeCoord(real32 ChunkDim, int32 *Tile, real32 *TileRel);
#endif
